#include "Camera.h"
#include "Timer.h"
#include "string.h"
#include "InputManager.h"
#include "ScreenSolution.h"
#include "math.h"

InputManager* imToCamara = NULL;

Camera::Camera(void)
{
	m_position = Vector3(0.0f, 0.0f, 0.0f);
	m_angleX = 0.0f;
	m_angleY = 0.0f;
	m_angleZ = 0.0f; 
	m_speed = 1.0f;
	m_id[0] = '\0';

	m_projectMatrix.SetPerspective(k_Pi / 4, ScreenSolution::GetInstance()->GetRatio(), 0.1f, 500.0f);
}

//---------------------------------------------------
/// INITIALIZE
//---------------------------------------------------
void Camera::Init(const char * id, const Vector3 & pos, GLfloat fovY, GLfloat aspect, GLfloat nearPlane, GLfloat farPlane, GLfloat speed)
{
	strcpy(m_id, id);
	SetPosition(pos);
	SetSpeed(speed);
	SetProjectMatrix(fovY, aspect, nearPlane, farPlane);

	imToCamara = InputManager::GetInstance();
}

//---------------------------------------------------
/// UPDATE
//---------------------------------------------------
void Camera::Update()
{
	if (imToCamara->IsDrag())
	{
		int dx = imToCamara->GetDx();
		int dy = imToCamara->GetDy();
		m_speed = sqrt(float (dx * dx + dy * dy)) / 4;

		if (dx < 0)
		{
			RotateCamera(LEFT);
		}
		else if (dx > 0)
		{
			RotateCamera(RIGHT);
		}

		if (dy < 0)
		{
			RotateCamera(UP);			
		}
		else if (dy > 0)
		{
			RotateCamera(DOWN);
		}
	}

	// movement
	if (imToCamara->IsKeyDown(38))
	{
		MoveCamera(FORWARD);
	}
	else if (imToCamara->IsKeyDown(40))
	{
		MoveCamera(BACKWARD);
	}

	if (imToCamara->IsKeyDown(37))
	{
		MoveCamera(LEFT);
	}
	else if (imToCamara->IsKeyDown(39))
	{
		MoveCamera(RIGHT);
	}

	/*if (imToCamara->IsKeyDown(87))
	{
		RotateCamera(UP);
	}
	else if (imToCamara->IsKeyDown(83))
	{
		RotateCamera(DOWN);
	}

	if (imToCamara->IsKeyDown(65))
	{
		RotateCamera(LEFT);
	}
	else if (imToCamara->IsKeyDown(68))
	{
		RotateCamera(RIGHT);
	}*/
}

//---------------------------------------------------
/// Camera movement
//---------------------------------------------------
void Camera::MoveCamera(Direction dir)
{
	GLfloat delta = Timer::FrameTime() * m_speed;
	Vector4 moveL;

	switch(dir)
	{
		case LEFT:
		{
			moveL = Vector4(-delta, 0, 0, 1);
		}
		break;

		case RIGHT:
		{
			moveL = Vector4(delta, 0, 0, 1);
		}
		break;

		case FORWARD:
		{
			moveL = Vector4(0, 0, -delta, 1);
		}
		break;

		case BACKWARD:
		{
			moveL = Vector4(0, 0, delta, 1);
		}
		break;

		default:
			LOGE("ERROR : unexpected value\n");
	}

	Vector4 moveW = moveL * CalculateWorldMatrix();
	m_position = Vector3(moveW.x, moveW.y, moveW.z);
}

//---------------------------------------------------
/// Camera rotation
//---------------------------------------------------
void Camera::RotateCamera(Direction dir)
{
	GLfloat a = Timer::FrameTime() * m_speed;

	switch(dir)
	{
		case UP:
		{
			m_angleX += a;
		}
		break;

		case DOWN:
		{
			m_angleX -= a;
		}
		break;

		case LEFT:
		{
			m_angleY += a;
		}
		break;

		case RIGHT:
		{
			m_angleY -= a;
		}
		break;

		default:
			LOGE("ERROR : unexpected value\n");
	}
}

Matrix Camera::CalculateWorldMatrix()
{
	Matrix trans, rot;

	trans.SetTranslation(m_position);
	rot.SetRotation(m_angleX, m_angleY, m_angleZ);

	return (rot * trans);
}

Matrix Camera::CalculateVPMatrix()
{
	Matrix rot, trans;

	trans.SetTranslation(-m_position);

	// Yaw-Pitch-Roll rule
	Matrix rotationXMatrix, rotationYMatrix, rotationZMatrix;
	rotationXMatrix.SetRotationX(-m_angleX);
	rotationYMatrix.SetRotationY(-m_angleY);
	rotationZMatrix.SetRotationZ(-m_angleZ);
	rot = rotationYMatrix * rotationXMatrix * rotationZMatrix;

	return (trans * rot * m_projectMatrix);
}

//---------------------------------------------------
/// These functions follow are setters
//---------------------------------------------------
void Camera::SetPosition(const Vector3 & pos)
{
	m_position = pos;
}

void Camera::SetSpeed(const GLfloat & speed)
{
	m_speed = speed;
}

void Camera::SetProjectMatrix(GLfloat fovY, GLfloat aspect, GLfloat nearPlane, GLfloat farPlane)
{
	m_near = nearPlane;
	m_far = farPlane;
	m_fovY = fovY;
	m_aspect = aspect;

	m_projectMatrix.SetPerspective(fovY, aspect, nearPlane, farPlane);
}

//---------------------------------------------------
/// Getters
//---------------------------------------------------
const char* Camera::GetId()
{
	return m_id;
}

Vector3 & Camera::GetPosition()
{
	return m_position;
}

//---------------------------------------------------
/// Deconstructor
//---------------------------------------------------
Camera::~Camera(void)
{
}
